本篇有一個區塊的code,是一些常見問題在code中的長相以及用法,裡面有一個非常陽春的名字檢測器,可以利用運行時,使用者輸入的名字來檢測。
code中會看到兩種符號#**
與@@
。
#**
部分就是常見問題。
請說明attr_accessor
今天內容有
類別變數,實體變數差異? 今天內容有
Ruby中self的意思?
請說明類別方法or實體方法差異
Ruby中private的用法
#@@
部分只是單純想分享。
initialize與new的差別? 今天內容有
參數加等號。 今天內容有
yield if block_given? 今天內容有
腳本區會一起說到以下內容gets.chomp()
,sleep的用途
。
外部腳本可不可以直接改物件的資料。
class Elden_ring
attr_accessor :name, :role, :power #**
@@bad_word = ["髒", "壞", "衰", "呵"] #**
def initialize(name = "殭屍", role = "反派", power = 20) #@@
@name = name
@role = role
@power = power
end
def named
yield if block_given? #@@
new_name = gets.chomp() #@@
if bad_name?(new_name)
puts "名字含有不好的字元或空白,請重新輸入"
named() #@@
else
self.name = new_name #**
puts %{是的! 原來他叫"#{self.name}"!} #@@
end
end
# class << self #類別方法 #**
# def fake_new
# Elden_ring.new("隱藏大魔王", "玩家", "unlimited")
# end
# end
# private #**
def bad_name?(new_name)
((new_name.split"") & @@bad_word) != [] || new_name == ""
end
end
##下面是腳本區。 也可將兩個區塊分開,利用`require`來運行
gwyn = Elden_ring.new("葛溫", "薪王", "is Over 9000!")
ash = Elden_ring.new("不死鎮雜魚", "戰士")
ash.bad_name?(ash.name) #測試private用
puts "#{gwyn.name},祂的職業是#{gwyn.role}。
他的力量#{gwyn.power}"
sleep 2 #@@
puts "\n有一天,有一個#{ash.name}。","是一個力量只有#{ash.power}的#{ash.role}雜魚。"
sleep 2
puts %{\n#{ash.name}它身為雜魚,平時非常羨慕"#{gwyn.name}"的存在
它希望自己能像"#{gwyn.name}"一般,成為'神族的一員'!
}
sleep 2
puts "%s\n%s" % ["這天,它做出了雜魚不可能會出的一件事!", "它坐下冥想...."]
sleep 2
["一年過去了...", "兩年過去了...", "三年...", "四年...", "整整五年的時間。"].each{|str|puts str; sleep 2}
ash.named do
prompt = '> '
puts "它想起了它擁有一個古老的名字!"
puts %{#請幫忙輸入新名字#}
print prompt
end
sleep 2
puts "感謝參與測試!"
# boss = Elden_ring.fake_new #測試類別方法用
# puts boss.name
# ash.fake_new
是一個Module類別定義出來的方法,由於許多類別常需有取得器(getter)
與設定器(setter)
的需求,以方便操作改寫資料與讀取,所以直接設計attr_accessor
方法,不是取代getter與setter,是會直接幫你生成這兩項的相對應方法,常直接寫在類別上方,也更清楚這類別中有哪些資料可以取得及設定。
如果自己設定getter
與setter
,名字隨意取也可以,但依照慣例盡量取相同例如下方示範,def name
就給@name
,這習慣到Rails後也是,保持這習慣,開發速度也會比較快。不用眼睛花掉還找不到當初自己設定的名字是什麼!
用途?
#取代getter與setter
class Fake_class
def initialize(name = "金三角")
@name = name
end
def name #getter methods,取得物件內資料。
@name
end
def name=(new_name) #setter methods,設定物件內資料。
@name = new_name
end
def change_name(name) #有setter 這類方法才能執行
@name = name
def
end
2.7.3 :052 > abc = Fake_class.new
=> #<Fake_class:0x00007f923942cc28 @name="金三角">
2.7.3 :053 > abc.name
=> "金三角"
2.7.3 :054 > abc.change_name("泰山")
=> "泰山"
2.7.3 :055 > abc.name
=> "泰山"
使用attr_accessor
後,就可以少設定
class Fake_class
attr_accessor :name
def initialize(name = "金三角")
@name = name
end
end
irb輸入完後可以輸入.methods,可以看到:name 與:name= 方法。
attr_writer
=> 只生成setter
attr_reader
=> 只生成getter
如果可以,不要因為懶惰而都只用attr_accessor
,有些資料確定只能讀取,而不希望被改變就使用attr_reader
,有些資料可以更動,但不希望被讀取那就attr_writer
,雖然進入框架後(不只是Rails),可能也不會再需要手動設定這些,但是知道自己哪些資料該怎麼處理的觀念還是要有。
不確定變數有幾種,可以請看看龍哥的,為你自己學Ruby on Rails:變數(variable)
少用類別變數
這是實體變數@variable
class Evil_soul
@name = "惡魔靈魂"
def self.name
p "#{@name}!"
end
end
2.7.3 :074 > Evil_soul.name
"惡魔靈魂!"
=> "惡魔靈魂!"
#建立一個子類別
class Dark_soul < Evil_soul
@name = "黑暗靈魂"
end
2.7.3 :078 > Dark_soul.name
"黑暗靈魂!"
=> "黑暗靈魂!"
兩個類別的@name不會互相干擾
即使繼承後也一樣。
2.7.3 :079 > Evil_soul.name
"惡魔靈魂!"
=> "惡魔靈魂!"
如果使用類別變數@@variable
class Evil_soul
@@name = "惡魔靈魂"
def self.name
p "#{@@name}!"
end
end
2.7.3 :087 > Evil_soul.name
"惡魔靈魂!"
=> "惡魔靈魂!"
class Dark_soul < Evil_soul
@@name = "黑暗靈魂"
end
#一被繼承,如果新類別也有用這個類別變數,類別變數馬上改變。
2.7.3 :091 > Evil_soul.name
"黑暗靈魂!"
=> "黑暗靈魂!"
少用類別變數比較好,避免類別被繼承後,類別變數會馬上改變,因為類別變數在同一條繼承鏈上所有的類別是共用的。實體變數作用在單一類別內,而且兩實體間互相不受影響。
當然更不適合直接用全域變數,這只是測試,我只放四個字,如果是很多內容的資料,應該放在別處。
initialize
與new
的差別?
先設定一個class。
class Init_test
def some(name = "測試", methods ="200種")
@name = name
@methods = methods
end
end
#這個類別new之後,只有給記憶體編號。
2.7.3 :118 > test = Init_test.new
=> #<Init_test:0x00007feb2a377dd8>
2.7.3 :068 > Init_test.some
Traceback (most recent call last):
NoMethodError (undefined method `some' for Init_test:Class)
#這樣設定some是實體方法,Init_test用不了。
可以發現,故意設定一種內容跟initiailize
方法,是毫無反應的。
class Init_test
def initialize(name = "測試", methods ="200種")
@name = name
@methods = methods
end
end
2.7.3 :126 > test = Init_test.new
=> #<Init_test:0x00007feb2a30e1f8 @name="測試", @methods="200種">
用initialize才有除了記憶體編號外,有其他"數值"存在。
也代表initialize與new有"聯動"。
#如果這樣是會動。
class Init_test
def new(name = "測試", methods ="200種")
@name = name
@methods = methods
end
end
2.7.3 :133 > Init_test.new
=> #<Init_test:0x00007feb2a366948 @name="測試", @methods="200種">
但這樣等於你把Ruby原本設定好的new
方法更動了,請不要這樣。
new
方法使用時,會自動調動initialize
方法,利用初始化來,建構物件的"長相",所以我們更動initialize
就會自動調整到new
方法。而如果直接動new
方法,那你得先確認new
方法裡會不會有你不知道的內容了。
initialize
在其他語言是建構子的概念(英語: Constructor,有時簡稱 ctor)
def some_method(num = "123")
num.to_i
end
這樣代表如果我們沒輸入參數,會自動以預設值處理。
2.7.3 :099 > some_method()
=> 123
2.7.3 :100 > some_method(567)
=> 567
#進入有連接資料庫的階段後,要更注意資料欄位的Type,以及自己設定的方法之使用對象。
2.7.3 :111 > some_method([1, 2, 3])
Traceback (most recent call last):
NoMethodError (undefined method `to_i' for [1, 2, 3]:Array)
雖然陽春,還是有幾個小小小細節說明一下。
原本code長這樣。
def named
prompt = '> '
puts "它想起了它擁有一個古老的名字!"
puts %{#請幫忙輸入新名字#}
print prompt
new_name = gets.chomp()
if ((new_name.split"") & $bad_word) != [] || new_name == ""
puts "名字含有不好的字元或空白,請重新輸入"
named()
else
self.name = new_name
puts %{是的! 原來他叫"#{self.name}"!}
end
end
#下面這四行,不是沒有意義,但我寫在方法裡,等於寫死了台詞。
prompt = '> '
puts "它想起了它擁有一個古老的名字!"
puts %{#請幫忙輸入新名字#}
print prompt
#而我使用yield出去後,如果我想換台詞,只需要在腳本區內用到的地方,其block區更改內容即可。
object.named {puts "這樣便利多了"}
yield if block_given?
與 named
方法又出現了named()
。方法內再執行一次同方法算是常見手法,等於是跑迴圈的一種,可以不用再看到loop
,while
,for..in
。用多了會發現很好用。
yield if block_given因為我只是請方法再幫我執行一次方法,我第二次並沒有給予block。yield
就是去block
。
要提這個是與rails裡的錯誤處理機制一樣,屬於比較柔性的處理方式。
回到code。
((new_name.split"") & $bad_word) != [] || new_name == ""
常用會用到的行為,不如直接多建立一個方法。
大家都會用到的行為,不如直接建立一個模組。
所以直接建立了一個bad_name?()
def bad_name?(new_name)
((new_name.split"") & @@bad_word) != [] || new_name == ""
end
可以看到整個檢驗器非常簡單,不是因為我檢測資料只有4個字,是因為功能只有拆開輸入的字串,再去與檢測資料比對,而如果是希望檢測名字內有沒有敏感的詞句,或敏感時事人物名字時,又該如何處理。那當然是我另外一篇文章的事了
明日從self繼續
今日的Leetcod.453. Minimum Moves to Equal Array Elements
題目連結:https://leetcode.com/problems/minimum-moves-to-equal-array-elements/
題目重點:一次可以動n-1
個元素來加1(今天是單純數學題。)
# @param {Integer[]} nums
# @return {Integer}
def min_moves(nums)
end
puts min_moves([1,2,3]) #=> 3
puts min_moves([1,1,1]) #=> 0
煉金術士題..
sum = 初始所有數組和
n = 數組長度
m = 增加次數
x = m次完後會等於的數字
x * n = 最後數組的總和
min = 數組中最小的數
關係如下
sum + m*(n - 1) = x * n
由於增加數都是1,所以最後會等於的數字x = min + m,帶入上一行公式。
sum + m*n - m = min*n + m*n
sum - m = min*n
m現在是我們唯一不知道的
sum - min*n = m
解完!
def min_moves(nums)
nums.sum - (nums.min * nums.size)
end
今天重點
1.說明attr_accessor
2.類別變數,實體變數差異?
3.Leetcod.453. Minimum Moves to Equal Array Elements